home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / ABUSESRC.ZIP / AbuseSrc / macabuse / imlib / port / mac / dirent.c < prev    next >
C/C++ Source or Header  |  1997-05-20  |  18KB  |  639 lines

  1. /****************************************************************************************
  2.  *
  3.  *    File:        dirent.c
  4.  *    Created:    7/3/93        By:    George T. Talbot
  5.  *    Purpose:    Implements UNIX-like directory reading for the Macintosh.
  6.  *
  7.  *    Modifications:
  8.  *
  9.  *    Notes:
  10.  *            1) These routines will NOT work under A/UX.
  11.  *            2) WD = working directory
  12.  *            3) CD = change directory
  13.  *            4) FS = file system
  14.  *            5) Mac filesystems allow spaces as part of pathnames!
  15.  *            6) All routines which return a path use the default Macintosh path separator,
  16.  *               a colon (":").
  17.  *
  18.  ****************************************************************************************/
  19.  
  20. #include "dirent.h"
  21. //#include <pascal.h>
  22. #include <string.h>
  23.  
  24. OSErr    dd_errno;                /*    Global errno to check after calls to dirent routines    */
  25. char    *dd_separator = ":";    /*    If you're feeling brave, change this to "/"    */
  26. int        dd_xform_seps = false;
  27.  
  28. /****************************************************************************************
  29.  *
  30.  *    This function, given a Macintosh-style pathname, will open a directory to that path.
  31.  *    NOTES:    1)    passing in nil will get you the current directory.
  32.  *            2)    ".:", "..:" & "┼:" are supported at the beginning of paths ONLY
  33.  *                by this routine.
  34.  *            3)    "/" will be turned into ":" by this routine.
  35.  *
  36.  *    Calls:            PBHGetVol(), PBHGetCatInfo(), PBHSetVol(), hopendir(), CtoPstr()
  37.  *    Called By:        <general purpose>
  38.  *    Globals Used:    dd_errno
  39.  *    Parameters:        pointer to C-string pathname or nil for current directory
  40.  *    Returns:        pointer to directory management block or nil & dd_errno will be set
  41.  *
  42.  ****************************************************************************************/
  43.  
  44. DIR    *opendir(char *dirname)
  45.     {
  46.     WDPBRec            pb;
  47.     CInfoPBRec        cpb;
  48.     short            vRefNum;
  49.     long            dirID;
  50.     char            *dname;
  51.     DIR                *temp;
  52.     char            path_temp[MAXPATHLEN+1];    /*    Temporary area for building pathname    */
  53.  
  54.     /*    Save the current path    */
  55.     pb.ioCompletion    = nil;
  56.     pb.ioNamePtr    = nil;
  57.     
  58.     if (dd_errno = PBHGetVol(&pb, false))
  59.         return nil;
  60.  
  61.     vRefNum    = pb.ioWDVRefNum;
  62.     dirID    = pb.ioWDDirID;
  63.  
  64.     /*    dname points to the desired pathname    */
  65.     dname    = dirname;
  66.  
  67.     /*    If no pathname was passed in, or there are no ".", ".." or "┼" special directory
  68.      *    names, then handle the pathname as normal.
  69.      */
  70.     if (dirname == nil)
  71.         goto opendir_fallthrough;
  72.  
  73.     /*    If there's not '.', '..' or '┼', fall through    */
  74.     if ((dirname[0] != '.') && (dirname[0] != '┼'))
  75.         goto opendir_fallthrough;
  76.  
  77.     /*    If there's a '┼', treat it like '..'    */
  78.     if (dirname[0] == '┼')
  79.         {
  80.         dname = &(dirname[1]);
  81.         goto path_dotdot;
  82.         }
  83.  
  84.     /*    If the pathname has "." (current directory) in front of it...    */
  85.     if (dirname[1] != '.')
  86.         {
  87.         /*    Skip over the "." and fall through    */
  88.         dname    = &(dirname[1]);
  89.         goto opendir_fallthrough;
  90.         }
  91.  
  92.     /*    Skip over the ".."    */
  93.     dname    = &(dirname[2]);
  94.  
  95. path_dotdot:
  96.     /*    If we get here, the directory has ".." in front of it...    */
  97.     
  98.     /*    First, get the directory info on the current directory.  We do this so
  99.      *    that we can get the directory's parent
  100.      */
  101.     cpb.dirInfo.ioCompletion    = nil;
  102.     cpb.dirInfo.ioNamePtr        = (unsigned char *)path_temp;    
  103.                                                 /* Unused, but must be set because of
  104.                                                  * bug in Apple File Sharing.
  105.                                                  */
  106.     cpb.dirInfo.ioVRefNum        = vRefNum;
  107.     cpb.dirInfo.ioFDirIndex        = -1;
  108.     cpb.dirInfo.ioDrDirID        = dirID;
  109.  
  110.     if (dd_errno = PBGetCatInfo(&cpb, false))
  111.         return nil;
  112.  
  113.     /*    Temporarily CD to the parent directory    */
  114.     pb.ioCompletion                = nil;
  115.     pb.ioNamePtr                = nil;
  116.     pb.ioVRefNum                = pb.ioWDVRefNum;
  117.     pb.ioWDDirID                = cpb.dirInfo.ioDrParID;
  118.     
  119.     if (dd_errno = PBHSetVol(&pb, false))
  120.         return nil;
  121.  
  122.     /*    This is the common code for all three cases above    */
  123. opendir_fallthrough:
  124.     /*    If the pathname is too long (this is a Macintosh FS constraint), then return    */
  125.     if (strlen(dname) > MAXPATHLEN)
  126.         {
  127.         /*    Set the error    */
  128.         dd_errno    = bdNamErr;
  129.         temp        = nil;
  130.         
  131.         /*    Go to the common exit, where we CD back to the saved WD    */
  132.         goto opendir_exit;
  133.         }
  134.  
  135.     /*    If this call was passed a pathname    */
  136.     if (dname != nil)
  137.         {
  138.         /*    Copy the pathname into a temp    */
  139.         strcpy(path_temp, dname);
  140.         
  141.         /*    Turn it into a Pascal string for the Mac FS    */
  142.         CtoPstr(path_temp);
  143.  
  144.         /*    Change any "/" to ":" for the Mac FS    */
  145.         if (dd_xform_seps)
  146.             {
  147.             int i;
  148.             
  149.             for (i=1; i<= path_temp[0]; ++i)
  150.                 if (path_temp[i] == '/')
  151.                     path_temp[i] = ':';
  152.             }
  153.  
  154.         /*    Try and open the directory    */
  155.         temp = hopendir(path_temp, 0, 0);
  156.         }
  157.     else
  158.         /*    If this call wasn't passed a pathname, then we call hopendir() with nil to
  159.          *    tell it to open the current working directory.
  160.          */
  161.         temp = hopendir(nil, 0, 0);
  162.  
  163.     /*    This is the common exit code which restores the current WD    */
  164. opendir_exit:
  165.     pb.ioCompletion                = nil;
  166.     pb.ioNamePtr                = nil;
  167.     pb.ioVRefNum                = vRefNum;
  168.     pb.ioWDDirID                = dirID;
  169.     
  170.     if (dd_errno = PBHSetVol(&pb, false))
  171.         {
  172.         /*    If this call failed, then get rid of the structures created by hopendir()    */
  173.         closedir(temp);
  174.         return nil;
  175.         }
  176.  
  177.     return temp;
  178.     }
  179.  
  180. /****************************************************************************************
  181.  *
  182.  *    This function actually opens the directory.  If you feel brave, you can call it.
  183.  *    If you pass in a dirname, then set vRefNum and dirID to 0.  All named opens are
  184.  *    relative to the current WD.  If you pass in vRefNum and dirID, then don't bother
  185.  *    passing in a name.  This routine WILL CHANGE YOUR CURRENT WORKING DIRECTORY!
  186.  *
  187.  *    Calls:            NewHandle(), PBHGetCatInfo(), PBHSetVol(), PtoCstr(), BlockMove(),
  188.  *                    DisposHandle(), MoveHHi(), HLock(), MemError()
  189.  *    Called By:        opendir(), and you if you feel brave.
  190.  *    Globals Used:    dd_errno
  191.  *    Parameters:        pointer to Pascal-string pathname, vRefNum, dirID of desired
  192.  *                    directory.  If you pass in a WDRefNum as the vRefNum, set dirID to 0
  193.  *    Returns:        pointer to directory management block or nil & dd_errno will be set
  194.  *
  195.  ****************************************************************************************/
  196.  
  197. DIR    *hopendir(char *dirname, short vRefNum, long dirID)
  198.     {
  199.     DIR                **curh, *cur;
  200.     CInfoPBRec        cpb;
  201.     WDPBRec            pb;
  202.     Str63            name;
  203.  
  204.     /*    Get memory for the directory structure    */
  205.     curh    = (DIR **) NewHandle(sizeof(DIR));
  206.  
  207.     /*    Did we get it?    */
  208.     if (curh == nil)
  209.         {
  210.         dd_errno    = MemError();
  211.         return nil;
  212.         }
  213.  
  214.     /*    Move it high and lock it    */
  215.     MoveHHi((Handle)curh);
  216.     HLock((Handle)curh);
  217.     cur        = *curh;
  218.  
  219.     /*    If we're supposed to open anything but the current directory, set the current
  220.      *    working directory to the desired directory.
  221.      */
  222.     if ((dirname != nil) || (vRefNum != 0) || (dirID != 0))
  223.         {
  224.         pb.ioCompletion                = nil;
  225.         pb.ioNamePtr                = (unsigned char *)dirname;
  226.         pb.ioVRefNum                = vRefNum;
  227.         pb.ioWDDirID                = dirID;
  228.         
  229.         if (dd_errno = PBHSetVol(&pb, false))
  230.             goto failure_exit;
  231.         }
  232.  
  233.     cur->dd_buf    = nil;
  234.     
  235.     /*    Get info on the desired directory (its name, etc.)    */
  236.     cpb.dirInfo.ioCompletion    = nil;
  237.     cpb.dirInfo.ioNamePtr        = name;
  238.     cpb.dirInfo.ioVRefNum        = vRefNum;
  239.     cpb.dirInfo.ioFDirIndex        = -1;
  240.     cpb.dirInfo.ioDrDirID        = dirID;
  241.     
  242.     if (dd_errno = PBGetCatInfo(&cpb, false))
  243.         goto failure_exit;
  244.  
  245.     /*    Save the directory info    */
  246.     cur->dir_fsp.vRefNum    = vRefNum;
  247.     cur->dd_fd                = cpb.dirInfo.ioDrDirID;
  248.     cur->dd_parent            = cpb.dirInfo.ioDrParID;
  249.  
  250.     BlockMove(name, cur->dir_fsp.name, sizeof(Str63));
  251.  
  252.     /*    Convert the name to a C-style string    */
  253.     PtoCstr(cur->dir_fsp.name);
  254.  
  255.     /*    Set up our directory structure to read the first entry    */
  256.     cur->dd_off                = 1;
  257.     cur->dd_numents            = cpb.dirInfo.ioDrNmFls;
  258.     cur->dd_cached            = false;
  259.  
  260.     return cur;
  261.  
  262.     /*    This code is branched-to in case of error.  It frees up the memory and returns.    */
  263. failure_exit:
  264.     DisposHandle((Handle) curh);
  265.     return nil;
  266.     }
  267.  
  268. /****************************************************************************************
  269.  *
  270.  *    This function returns the index of the directory entry to be next read.
  271.  *
  272.  *    Calls:            nothing
  273.  *    Called By:        <general purpose>
  274.  *    Globals Used:    none
  275.  *    Parameters:        pointer to the directory management block
  276.  *    Returns:        index of the next directory entry to be read.
  277.  *
  278.  ****************************************************************************************/
  279.  
  280. long    telldir(DIR *dirp)
  281.     {
  282.     if (dirp->dd_off > dirp->dd_numents)
  283.         return -1;
  284.     else
  285.         return dirp->dd_off-1;    /* The -1 is because Macs start at 1 & not 0 for dir index,
  286.                                  * and this is a little more POSIX.
  287.                                  */
  288.     }
  289.  
  290. /****************************************************************************************
  291.  *
  292.  *    This function closes the directory opened with opendir() or hopendir()
  293.  *
  294.  *    Calls:            DisposHandle(), RecoverHandle()
  295.  *    Called By:        <general purpose>
  296.  *    Globals Used:    none
  297.  *    Parameters:        pointer to the directory management block
  298.  *    Returns:        0 (always successful)
  299.  *
  300.  ****************************************************************************************/
  301.  
  302. int    closedir(DIR *dirp)
  303.     {
  304.     struct dirent    **cur;
  305.     
  306.     /*    Dispose of any directory entries read in.    */
  307.     cur    = dirp->dd_buf;
  308.     
  309.     dd_errno    = noErr;
  310.  
  311.     while (cur)
  312.         {
  313.         struct dirent    **next;
  314.         
  315.         next    = (*cur)->next;
  316.         
  317.         DisposHandle((Handle) cur);
  318.         
  319.         if (dd_errno == noErr)
  320.             dd_errno    = MemError();
  321.  
  322.         cur        = next;
  323.         }
  324.  
  325.     /*    Dispose of the directory managment block    */
  326.     DisposHandle(RecoverHandle((Ptr) dirp));
  327.  
  328.     if (dd_errno == noErr)
  329.         dd_errno    = MemError();
  330.  
  331.     return dd_errno?-1:0;
  332.     }
  333.  
  334. /****************************************************************************************
  335.  *
  336.  *    This function sets the index of the next-read directory entry.  It will also search
  337.  *    the list of read entries so that an entry won't be read from disk more than once.
  338.  *
  339.  *    Calls:            nothing
  340.  *    Called By:        <general purpose>
  341.  *    Globals Used:    none
  342.  *    Parameters:        pointer to the directory management block, index of directory
  343.  *    Returns:        nothing
  344.  *
  345.  ****************************************************************************************/
  346.  
  347. void    seekdir(DIR *dirp, long loc)
  348.     {
  349.     struct dirent    **cur;
  350.     
  351.     dirp->dd_off        = loc+1;    /* The +1 is because the Mac indexes directories
  352.                                      * from 1 and not 0 and we want to be a little bit
  353.                                      * POSIX
  354.                                      */
  355.  
  356.     /*    Search through the entries that we've read already    */
  357.     cur    = dirp->dd_buf;
  358.     
  359.     while (cur)
  360.         {
  361.         /*    If we find the entry that we've seeked to, set up so that readdir() will
  362.          *    return this one instead of reading a new one.
  363.          */
  364.         if (loc == (*cur)->d_off)
  365.             {
  366.             dirp->dd_cached        = true;
  367.             dirp->dd_cache_hint    = cur;
  368.  
  369.             return;
  370.             }
  371.  
  372.         cur    = (*cur)->next;
  373.         }
  374.  
  375.     /*    If we didn't find it, then tell readdir() to get the entry from the FS    */
  376.     dirp->dd_cached    = false;
  377.     }
  378.  
  379. /****************************************************************************************
  380.  *
  381.  *    This function will read the next directory entry from disk.  It will return nil and
  382.  *    set dd_errno to noErr when the end of the directory is reached.  It will avoid
  383.  *    reading directory entries from disk more than once.
  384.  *
  385.  *    Calls:            nothing
  386.  *    Called By:        <general purpose>
  387.  *    Globals Used:    none
  388.  *    Parameters:        pointer to the directory management block
  389.  *    Returns:        pointer to directory entry or nil if an error occurred and dd_errno
  390.  *                    will be set.  If the last entry has already been read, this will
  391.  *                    return nil and dd_errno will be set to noErr.
  392.  *
  393.  ****************************************************************************************/
  394.  
  395. struct dirent    *readdir(DIR *dirp)
  396.     {
  397.     CInfoPBRec        cpb;
  398.     struct dirent    **meh, *me;
  399.     
  400.     /*    If the entry has been read already, then return the already present entry    */
  401.     if (dirp->dd_cached)
  402.         me    = *(dirp->dd_cache_hint);
  403.     else
  404.         /*    Otherwise, read it from disk...    */
  405.         {
  406.         /*    Past the end of the directory?    */
  407.         if (dirp->dd_off > dirp->dd_numents)
  408.             {
  409.             dd_errno    = noErr;
  410.             return nil;
  411.             }
  412.  
  413.         /*    Allocate space for a new entry    */
  414.         meh    = (struct dirent **) NewHandle(sizeof(struct dirent));
  415.         
  416.         /*    Enough memory?    */
  417.         if (meh == nil)
  418.             {
  419.             dd_errno    = MemError();
  420.             return nil;
  421.             }
  422.  
  423.         /*    Lock the entry    */
  424.         MoveHHi((Handle) meh);
  425.         HLock((Handle) meh);
  426.  
  427.         me    = *meh;
  428.  
  429.         /*    Get the entry's info from disk    */
  430.         me->fsp.name[0]                = 0;
  431.  
  432.         cpb.dirInfo.ioCompletion    = nil;
  433.         cpb.dirInfo.ioNamePtr        = me->fsp.name;
  434.         cpb.dirInfo.ioVRefNum        = dirp->dir_fsp.vRefNum;
  435.         cpb.dirInfo.ioFDirIndex        = dirp->dd_off;
  436.         cpb.dirInfo.ioDrDirID        = dirp->dd_fd;
  437.  
  438.         if (dd_errno = PBGetCatInfo(&cpb, false))
  439.             {
  440.             DisposHandle((Handle) meh);
  441.             return nil;
  442.             }
  443.     
  444.         /*    Set up the dirent structure    */
  445.         me->d_off            = dirp->dd_off-1;
  446.         me->fsp.vRefNum        = cpb.dirInfo.ioVRefNum;
  447.         me->d_fileno        = cpb.dirInfo.ioDrDirID;
  448.         me->d_parent        = cpb.dirInfo.ioDrParID;
  449.         
  450.         /*    C strings only!    */
  451.         PtoCstr(me->fsp.name);
  452.  
  453.         /*    Add it to the list for this directory    */
  454.         me->next            = dirp->dd_buf;
  455.         
  456.         dirp->dd_buf        = meh;
  457.         }
  458.  
  459.     /*    Seek to the next entry    */
  460.     seekdir(dirp, dirp->dd_off);
  461.  
  462.     /*    Return what we've found    */
  463.     return me;
  464.     }
  465.  
  466. /****************************************************************************************
  467.  *
  468.  *    This function will give an absolute pathname to a given directory.
  469.  *
  470.  *    Calls:            NewPtr(), DisposPtr(), PBGetCatInfo()
  471.  *    Called By:        <general purpose>
  472.  *    Globals Used:    none
  473.  *    Parameters:        vRefNum and startDirID of desired path, pointer to path name storage,
  474.  *                    length of path name storage, pointer to C-string separator.
  475.  *    Returns:        bdNamErr if the path would overflow the storage,
  476.  *                    some other error code if something else happened,
  477.  *                    or noErr on success.
  478.  *
  479.  ****************************************************************************************/
  480.  
  481. OSErr    hgetwd(short vRefNum, long startDirID, char *path, int max_path_len, char *sep)
  482.     {
  483.     long        curDirID;
  484.     OSErr        err;
  485.     CInfoPBRec    pb;
  486.     Str63        name;
  487.     char        *temp_path;
  488.  
  489.     /*    Start with an empty path    */
  490.     path[0]    = 0;
  491.  
  492.     /*    Get memory for a temporary path    */
  493.     temp_path    = (char *) NewPtr(max_path_len);
  494.     
  495.     if (temp_path == nil)
  496.         return MemError();
  497.  
  498.     /*    Start at the given directory    */
  499.     curDirID    = startDirID;
  500.  
  501.     do    {
  502.         /*    Get cat info for the current directory    */
  503.         name[0]    = 0;
  504.  
  505.         pb.dirInfo.ioCompletion    = nil;
  506.         pb.dirInfo.ioNamePtr    = name;
  507.         pb.dirInfo.ioVRefNum    = vRefNum;
  508.         pb.dirInfo.ioFDirIndex    = -1;
  509.         pb.dirInfo.ioDrDirID    = curDirID;
  510.         
  511.         if (err = PBGetCatInfo(&pb, false))
  512.             {
  513.             DisposPtr((Ptr) temp_path);
  514.             return err;
  515.             }
  516.  
  517.         /*    Convert name to a C string    */
  518.         PtoCstr(name);
  519.  
  520.         /*    Check that we don't overflow storage    */
  521.         if ((strlen((char*)name) + strlen(path) + strlen(sep)) >= max_path_len)
  522.             {
  523.             DisposPtr((Ptr) temp_path);
  524.             return bdNamErr;
  525.             }
  526.  
  527.         /*    Prepend the name and separator    */
  528.         strcpy(temp_path, path);
  529.         strcpy(path, (char*)name);
  530.         strcat(path, sep);
  531.         strcat(path, temp_path);
  532.  
  533.         /*    Move "up" one directory    */
  534.         curDirID    = pb.dirInfo.ioDrParID;
  535.         }
  536.     /*    Until we hit the root directory    */
  537.     while (pb.dirInfo.ioDrDirID != fsRtDirID);
  538.  
  539.     /*    Get rid of our temp storage and return    */
  540.     DisposPtr((Ptr) temp_path);
  541.  
  542.     return MemError();
  543.     }
  544.  
  545. /****************************************************************************************
  546.  *
  547.  *    This function will change the current working directory.
  548.  *
  549.  *    Calls:            opendir(), closedir(), PBHSetVol()
  550.  *    Called By:        <general purpose>
  551.  *    Globals Used:    none
  552.  *    Parameters:        C-string pathname.
  553.  *    Returns:        -1 on failure, 0 on success.  Sets dd_errno on failure.
  554.  *
  555.  ****************************************************************************************/
  556.  
  557. int    chdir(char *path)
  558.     {
  559.     DIR        *d;
  560.     short    vRefNum;
  561.     long    dirID;
  562.     WDPBRec    pb;
  563.  
  564.     /*    Open the directory    */
  565.     d    = opendir(path);
  566.     
  567.     if (d == nil)
  568.         return -1;
  569.  
  570.     /*    Get the Mac FS identification for this directory    */
  571.     vRefNum    = d->dd_volume;
  572.     dirID    = d->dd_fd;
  573.     
  574.     /*    Close the directory    */
  575.     closedir(d);
  576.  
  577.     /*    CD to the new directory    */
  578.     pb.ioCompletion    = nil;
  579.     pb.ioNamePtr    = nil;
  580.     pb.ioVRefNum    = vRefNum;
  581.     pb.ioWDDirID    = dirID;
  582.     
  583.     dd_errno = PBHSetVol(&pb, false);
  584.     
  585.     return dd_errno?-1:0;
  586.     }
  587.  
  588. /****************************************************************************************
  589.  *
  590.  *    This function will get the current working directory's path.
  591.  *
  592.  *    Calls:            PBHGetVol(), hgetwd()
  593.  *    Called By:        <general purpose>
  594.  *    Globals Used:    none
  595.  *    Parameters:        pointer to a buffer of MAXPATHLEN bytes.
  596.  *    Returns:        pointer to the buffer on success, on failure, nil and dd_errno will
  597.  *                    be set.
  598.  *
  599.  ****************************************************************************************/
  600.  
  601. char *getwd(char *path)
  602.     {
  603.     WDPBRec    pb;
  604.  
  605.     /*    Get the current working directory    */
  606.     pb.ioCompletion    = nil;
  607.     pb.ioNamePtr    = nil;
  608.     
  609.     if (dd_errno = PBHGetVol(&pb, false))
  610.         return nil;
  611.  
  612.     /*    Transform it into a path    */
  613.     if (dd_errno = hgetwd(pb.ioWDVRefNum, pb.ioWDDirID, path, MAXPATHLEN-1, dd_separator))
  614.         return nil;
  615.  
  616.     return path;
  617.     }
  618.  
  619. /****************************************************************************************
  620.  *
  621.  *    This function will get the path to a given (already opened) directory.
  622.  *
  623.  *    Calls:            hgetwd()
  624.  *    Called By:        <general purpose>
  625.  *    Globals Used:    none
  626.  *    Parameters:        pointer to a buffer of MAXPATHLEN bytes.
  627.  *    Returns:        pointer to the buffer on success, on failure, nil and dd_errno will
  628.  *                    be set.
  629.  *
  630.  ****************************************************************************************/
  631.  
  632. char *pathdir(DIR *dirp, char *path)
  633.     {
  634.     if (dd_errno = hgetwd(dirp->dd_volume, dirp->dd_fd, path, MAXPATHLEN-1, dd_separator))
  635.         return nil;
  636.  
  637.     return path;
  638.     }
  639.